use super::{err, err_value, ECode, MapSv, TmLocal, TmOffset, VecV};
use chrono::prelude::*;
use chrono::Duration;
use serde_json::Value;
use std::collections::HashMap;

macro_rules! miss {
    ($x:expr) => {
        err_value(ECode::MissKey, $x)
    };
}

macro_rules! miss_empty {
    ($x:expr) => {
        err_value(ECode::MissOrEmpty, $x)
    };
}

macro_rules! efmt {
    ($x:expr) => {
        err_value(ECode::JsonFormat, $x)
    };
}

pub trait VBase {
    fn kstr(&self) -> Option<&str>;
    fn ki64(&self) -> Option<i64>;
    fn kbool(&self) -> Option<bool>;
    fn kf64(&self) -> Option<f64>;
    fn kstr_trim(&self) -> Option<&str>;
    fn array(&self) -> Option<&VecV>;
    fn kobj(&self) -> Option<&MapSv>;
    fn kobj_mut(&mut self) -> Option<&mut MapSv>;
    fn array_mut(&mut self) -> Option<&mut VecV>;

    fn kstr_empty(&self) -> Option<&str> {
        let v = self.kstr()?;

        if v.is_empty() {
            None
        } else {
            Some(v)
        }
    }

    fn first_obj(&self) -> Option<&MapSv> {
        let v = self.array()?;
        v[0].as_object()
    }

    fn ckstr(&self) -> Result<&str, Value> {
        self.kstr().ok_or(efmt!("str"))
    }

    fn ckbool(&self) -> Result<bool, Value> {
        self.kbool().ok_or(efmt!("bool"))
    }

    fn ckf64(&self) -> Result<f64, Value> {
        self.kf64().ok_or(efmt!("f64"))
    }

    fn cki64(&self) -> Result<i64, Value> {
        self.ki64().ok_or(efmt!("i64"))
    }

    fn ckstr_empty(&self) -> Result<&str, Value> {
        self.kstr_empty().ok_or(efmt!("cannot be empty"))
    }

    fn ckarray(&self) -> Result<&VecV, Value> {
        self.array().ok_or(efmt!("array"))
    }

    fn ckarray_mut(&mut self) -> Result<&mut VecV, Value> {
        self.array_mut().ok_or(efmt!("array"))
    }

    fn ckfirst_obj(&self) -> Result<&MapSv, Value> {
        self.first_obj().ok_or(efmt!("array first"))
    }

    fn ckobj(&self) -> Result<&MapSv, Value> {
        self.kobj().ok_or(efmt!("obj"))
    }

    fn ckobj_mut(&mut self) -> Result<&mut MapSv, Value> {
        self.kobj_mut().ok_or(efmt!("obj"))
    }
}

pub trait VMap {
    fn k_bool(&self, key: &str) -> Option<bool>;
    fn k_i64(&self, key: &str) -> Option<i64>;
    fn k_f64(&self, key: &str) -> Option<f64>;
    fn k_str(&self, key: &str) -> Option<&str>;
    fn k_str_trim(&self, key: &str) -> Option<&str>;
    fn k_obj(&self, key: &str) -> Option<&MapSv>;
    fn k_array(&self, key: &str) -> Option<&VecV>;
    fn k_val(&self, key: &str) -> Option<&Value>;
    fn k_val_mut(&mut self, key: &str) -> Option<&mut Value>;
    fn k_array_mut(&mut self, key: &str) -> Option<&mut VecV>;
    fn k_obj_mut(&mut self, key: &str) -> Option<&mut MapSv>;

    fn k_str_empty(&self, key: &str) -> Option<&str> {
        let v = self.k_str(key)?;
        if v.is_empty() {
            None
        } else {
            Some(v)
        }
    }

    fn k_str_empty_trim(&self, key: &str) -> Option<&str> {
        let v = self.k_str_trim(key)?;
        if v.is_empty() {
            None
        } else {
            Some(v)
        }
    }

    fn ck_str_empty(&self, key: &str) -> Result<&str, Value> {
        self.k_str_empty(key).ok_or(miss_empty!(key))
    }

    fn ck_str_empty_trim(&self, key: &str) -> Result<&str, Value> {
        self.k_str_empty_trim(key).ok_or(miss_empty!(key))
    }

    fn k_first_obj(&self, key: &str) -> Option<&MapSv> {
        let v = self.k_array(key)?;
        v[0].as_object()
    }

    fn ck_first_obj(&self, key: &str) -> Result<&MapSv, Value> {
        self.k_first_obj(key).ok_or(miss!(key))
    }

    fn ck_str(&self, key: &str) -> Result<&str, Value> {
        self.k_str(key).ok_or(miss!(key))
    }

    fn ck_str_trim(&self, key: &str) -> Result<&str, Value> {
        self.k_str_trim(key).ok_or(miss!(key))
    }

    fn ck_obj(&self, key: &str) -> Result<&MapSv, Value> {
        self.k_obj(key).ok_or(miss!(key))
    }

    fn ck_obj_mut(&mut self, key: &str) -> Result<&mut MapSv, Value> {
        self.k_obj_mut(key).ok_or(miss!(key))
    }

    fn ck_array(&self, key: &str) -> Result<&VecV, Value> {
        self.k_array(key).ok_or(miss!(key))
    }

    fn ck_array_mut(&mut self, key: &str) -> Result<&mut VecV, Value> {
        self.k_array_mut(key).ok_or(miss!(key))
    }

    fn ck_first(&self, key: &str) -> Result<&Value, Value> {
        let v = self.ck_array(key)?;
        Ok(&v[0])
    }

    fn k_first(&self, key: &str) -> Option<&Value> {
        let v = self.k_array(key)?;
        Some(&v[0])
    }

    fn ck_bool(&self, key: &str) -> Result<bool, Value> {
        self.k_bool(key).ok_or(miss!(key))
    }

    fn ck_i64(&self, key: &str) -> Result<i64, Value> {
        self.k_i64(key).ok_or(miss!(key))
    }

    fn ck_f64(&self, key: &str) -> Result<f64, Value> {
        self.k_f64(key).ok_or(miss!(key))
    }

    fn ck_val(&self, key: &str) -> Result<&Value, Value> {
        self.k_val(key).ok_or(miss!(key))
    }
}

impl VMap for Value {
    fn k_obj(&self, key: &str) -> Option<&MapSv> {
        self.k_val(key)?.as_object()
    }

    fn k_obj_mut(&mut self, key: &str) -> Option<&mut MapSv> {
        self.k_val_mut(key)?.as_object_mut()
    }

    fn k_str(&self, key: &str) -> Option<&str> {
        self.k_val(key)?.kstr()
    }

    fn k_str_trim(&self, key: &str) -> Option<&str> {
        self.k_val(key)?.kstr_trim()
    }

    fn k_array(&self, key: &str) -> Option<&VecV> {
        self.k_val(key)?.array()
    }

    fn k_array_mut(&mut self, key: &str) -> Option<&mut VecV> {
        self.k_val_mut(key)?.array_mut()
    }

    fn k_bool(&self, key: &str) -> Option<bool> {
        self.k_val(key)?.as_bool()
    }

    fn k_i64(&self, key: &str) -> Option<i64> {
        let v = self.k_val(key)?;
        match v.as_i64() {
            Some(v) => Some(v),
            None => {
                let d = v.kstr()?;
                let d: i64 = d.parse().ok()?;
                Some(d)
            }
        }
    }

    fn k_f64(&self, key: &str) -> Option<f64> {
        let v = self.k_val(key)?;
        match v.as_f64() {
            Some(v) => Some(v),
            None => {
                let d = v.kstr()?;
                let d: f64 = d.parse().ok()?;
                Some(d)
            }
        }
    }

    fn k_val(&self, key: &str) -> Option<&Value> {
        self.as_object()?.get(key)
    }

    fn k_val_mut(&mut self, key: &str) -> Option<&mut Value> {
        self.as_object_mut()?.get_mut(key)
    }
}

impl VBase for Value {
    fn array(&self) -> Option<&VecV> {
        let v = self.as_array()?;
        if v.is_empty() {
            None
        } else {
            Some(v)
        }
    }

    fn array_mut(&mut self) -> Option<&mut VecV> {
        self.as_array_mut()
    }

    fn kobj(&self) -> Option<&MapSv> {
        self.as_object()
    }

    fn kobj_mut(&mut self) -> Option<&mut MapSv> {
        self.as_object_mut()
    }

    fn kstr(&self) -> Option<&str> {
        let v = self.as_str()?.trim_matches('\"');
        Some(v)
    }

    fn kbool(&self) -> Option<bool> {
        self.as_bool()
    }

    fn kf64(&self) -> Option<f64> {
        self.as_f64()
    }

    fn ki64(&self) -> Option<i64> {
        self.as_i64()
    }

    fn kstr_trim(&self) -> Option<&str> {
        let v = self.as_str()?.trim().trim_matches('\"');
        Some(v)
    }
}

impl VMap for MapSv {
    fn k_obj(&self, key: &str) -> Option<&MapSv> {
        self.get(key)?.as_object()
    }

    fn k_obj_mut(&mut self, key: &str) -> Option<&mut MapSv> {
        self.get_mut(key)?.as_object_mut()
    }

    fn k_str(&self, key: &str) -> Option<&str> {
        self.get(key)?.kstr()
    }

    fn k_str_trim(&self, key: &str) -> Option<&str> {
        self.get(key)?.kstr_trim()
    }

    fn k_array(&self, key: &str) -> Option<&VecV> {
        self.get(key)?.array()
    }

    fn k_array_mut(&mut self, key: &str) -> Option<&mut VecV> {
        self.get_mut(key)?.array_mut()
    }

    fn k_bool(&self, key: &str) -> Option<bool> {
        self.get(key)?.as_bool()
    }

    fn k_i64(&self, key: &str) -> Option<i64> {
        let v = self.get(key)?;
        match v.as_i64() {
            Some(v) => Some(v),
            None => {
                let d = v.kstr()?;
                let d: i64 = d.parse().ok()?;
                Some(d)
            }
        }
    }

    fn k_f64(&self, key: &str) -> Option<f64> {
        let v = self.get(key)?;
        match v.as_f64() {
            Some(v) => Some(v),
            None => {
                let d = v.kstr()?;
                let d: f64 = d.parse().ok()?;
                Some(d)
            }
        }
    }

    fn k_val(&self, key: &str) -> Option<&Value> {
        self.get(key)
    }

    fn k_val_mut(&mut self, key: &str) -> Option<&mut Value> {
        self.get_mut(key)
    }
}

impl VBase for VecV {
    fn array(&self) -> Option<&VecV> {
        Some(&self)
    }

    fn array_mut(&mut self) -> Option<&mut VecV> {
        Some(self)
    }

    fn ckobj(&self) -> Result<&MapSv, Value> {
        Err(efmt!("array not obj"))
    }

    fn ckobj_mut(&mut self) -> Result<&mut MapSv, Value> {
        Err(efmt!("array not obj"))
    }

    fn ckarray(&self) -> Result<&VecV, Value> {
        Ok(&self)
    }

    fn first_obj(&self) -> Option<&MapSv> {
        if self.is_empty() {
            None
        } else {
            self[0].as_object()
        }
    }

    fn kstr(&self) -> Option<&str> {
        None
    }

    fn ki64(&self) -> Option<i64> {
        None
    }

    fn kstr_trim(&self) -> Option<&str> {
        None
    }

    fn kbool(&self) -> Option<bool> {
        None
    }

    fn kf64(&self) -> Option<f64> {
        None
    }

    fn kobj(&self) -> Option<&MapSv> {
        None
    }

    fn kobj_mut(&mut self) -> Option<&mut MapSv> {
        None
    }
}

impl VMap for HashMap<String, String> {
    fn k_str(&self, key: &str) -> Option<&str> {
        let v = self.get(key)?;
        Some(v.as_str())
    }

    fn k_i64(&self, _key: &str) -> Option<i64> {
        None
    }

    fn k_f64(&self, _key: &str) -> Option<f64> {
        None
    }

    fn k_str_trim(&self, _key: &str) -> Option<&str> {
        None
    }

    fn k_obj(&self, _key: &str) -> Option<&MapSv> {
        None
    }

    fn k_obj_mut(&mut self, _key: &str) -> Option<&mut MapSv> {
        None
    }

    fn k_array(&self, _key: &str) -> Option<&VecV> {
        None
    }

    fn k_array_mut(&mut self, _key: &str) -> Option<&mut VecV> {
        None
    }

    fn k_val(&self, _key: &str) -> Option<&Value> {
        None
    }

    fn k_bool(&self, _key: &str) -> Option<bool> {
        None
    }

    fn k_val_mut(&mut self, _key: &str) -> Option<&mut Value> {
        None
    }
}

impl VMap for HashMap<String, Value> {
    fn k_bool(&self, key: &str) -> Option<bool> {
        self.get(key)?.as_bool()
    }

    fn k_i64(&self, key: &str) -> Option<i64> {
        let v = self.get(key)?;
        match v.as_i64() {
            Some(v) => Some(v),
            None => {
                let d = v.kstr()?;
                let d: i64 = d.parse().ok()?;
                Some(d)
            }
        }
    }

    fn k_f64(&self, key: &str) -> Option<f64> {
        let v = self.get(key)?;
        match v.as_f64() {
            Some(v) => Some(v),
            None => {
                let d = v.kstr()?;
                let d: f64 = d.parse().ok()?;
                Some(d)
            }
        }
    }

    fn k_str(&self, key: &str) -> Option<&str> {
        self.get(key)?.kstr()
    }

    fn k_str_trim(&self, key: &str) -> Option<&str> {
        self.get(key)?.kstr_trim()
    }

    fn k_obj(&self, key: &str) -> Option<&MapSv> {
        self.get(key)?.as_object()
    }

    fn k_obj_mut(&mut self, key: &str) -> Option<&mut MapSv> {
        self.get_mut(key)?.as_object_mut()
    }

    fn k_array(&self, key: &str) -> Option<&VecV> {
        self.get(key)?.array()
    }

    fn k_array_mut(&mut self, key: &str) -> Option<&mut VecV> {
        self.get_mut(key)?.array_mut()
    }

    fn k_val(&self, key: &str) -> Option<&Value> {
        self.get(key)
    }

    fn k_val_mut(&mut self, key: &str) -> Option<&mut Value> {
        self.get_mut(key)
    }
}

pub trait Base {
    // YYYY-MM-DD HH:MM:SS
    fn from_iso(&self, val: &str) -> Result<TmOffset, Value> {
        let tm = format!("{}+0000", val);
        DateTime::parse_from_str(&tm, "%Y-%m-%d %H:%M:%S%z").or_else(|e| err!(ECode::Time, e))
    }

    fn utc_time(&self, time: &str) -> Result<TmOffset, Value> {
        DateTime::parse_from_rfc3339(time).or_else(|e| err!(ECode::Time, e))
    }

    // sub milliseconds
    fn pre_cur_time(&self, m: i64, fmt: &str) -> Result<String, Value> {
        pre_cur_time(m, fmt)
    }

    // add day
    fn pre_cur_time_day(&self, d: i64, fmt: &str) -> Result<String, Value> {
        let now: TmLocal = Local::now();
        let diff = Duration::days(d);

        let ret = now
            .checked_sub_signed(diff)
            .ok_or(err_value(ECode::Time, "time error"))?;

        Ok(ret.format(fmt).to_string())
    }

    // add day
    fn next_cur_time_day(&self, d: i64, fmt: &str) -> Result<String, Value> {
        let now: TmLocal = Local::now();
        let diff = Duration::days(d);

        let ret = now
            .checked_add_signed(diff)
            .ok_or(err_value(ECode::Time, "time error"))?;

        Ok(ret.format(fmt).to_string())
    }

    // utc offset for hours
    fn utc_offset_from_zero(&self) -> f64 {
        utc_offset_from_zero()
    }

    fn cur_time_timestamp(&self) -> i64 {
        cur_time_timestamp()
    }

    // %Y-%m-%d %H:%M:%S <-> YYYY-MM-DD HH:MM:SS
    fn cur_time(&self, fmt: &str) -> String {
        cur_time(fmt)
    }
}

pub fn cur_time_timestamp() -> i64 {
    let now: DateTime<Local> = Local::now();
    now.timestamp_millis()
}

// %Y-%m-%d %H:%M:%S <-> YYYY-MM-DD HH:MM:SS
pub fn cur_time(fmt: &str) -> String {
    let now: TmLocal = Local::now();
    now.format(fmt).to_string()
}

pub fn pre_cur_time(m: i64, fmt: &str) -> Result<String, Value> {
    let now: TmLocal = Local::now();
    let diff = Duration::milliseconds(m);

    let ret = now
        .checked_sub_signed(diff)
        .ok_or(err_value(ECode::Time, "time error"))?;

    Ok(ret.format(fmt).to_string())
}

pub fn pre_cur_day(m: i64, fmt: &str) -> Result<String, Value> {
    let now: TmLocal = Local::now();
    let diff = Duration::days(m);

    let ret = now
        .checked_sub_signed(diff)
        .ok_or(err_value(ECode::Time, "time error"))?;

    Ok(ret.format(fmt).to_string())
}

// utc offset for hours
pub fn utc_offset_from_zero() -> f64 {
    let ret = Local::now().offset().local_minus_utc() as f64;
    ret / 3600.0
}
